// Popover.js

'use strict'

import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {StyleSheet, View, ViewPropTypes, PixelRatio} from 'react-native'
const pixelSize = (function () {
  let pixelRatio = PixelRatio.get()
  if (pixelRatio >= 3) return 0.333
  else if (pixelRatio >= 2) return 0.5
  else return 1
})()
export default class Popover extends Component {
  static propTypes = {
    ...ViewPropTypes,
    arrow: PropTypes.oneOf([
      'none',
      'topLeft',
      'top',
      'topRight',
      'rightTop',
      'right',
      'rightBottom',
      'bottomRight',
      'bottom',
      'bottomLeft',
      'leftBottom',
      'left',
      'leftTop'
    ]),
    paddingCorner: PropTypes.number
  };

  static defaultProps = {
    ...View.defaultProps,
    arrow: 'none'
  };

  constructor (props) {
    super(props)
    this.state = {
      width: 0,
      height: 0
    }
  }

  filterPopoverStyle (fs, includeRadius) {
    let {flexDirection, alignItems, justifyContent, margin, marginBottom, marginHorizontal, marginLeft, marginRight, marginTop, marginVertical, padding, paddingBottom, paddingHorizontal, paddingLeft, paddingRight, paddingTop, paddingVertical, backgroundColor, borderBottomColor, borderBottomLeftRadius, borderBottomRightRadius, borderBottomWidth, borderColor, borderLeftColor, borderLeftWidth, borderRadius, borderRightColor, borderRightWidth, borderStyle, borderTopColor, borderTopLeftRadius, borderTopRightRadius, borderTopWidth, borderWidth, ...others} = fs
    let style = includeRadius ? {borderBottomLeftRadius, borderBottomRightRadius, borderRadius, borderTopLeftRadius, borderTopRightRadius, ...others} : {...others}
    for (let key in style) {
      if (style[key] === undefined) {
        delete style[key]
      }
    }
    return style
  }

  filterContentStyle (fs) {
    let {flexDirection, alignItems, justifyContent, margin, marginBottom, marginHorizontal, marginLeft, marginRight, marginTop, marginVertical, padding, paddingBottom, paddingHorizontal, paddingLeft, paddingRight, paddingTop, paddingVertical, backgroundColor, borderBottomColor, borderBottomLeftRadius, borderBottomRightRadius, borderBottomWidth, borderColor, borderLeftColor, borderLeftWidth, borderRadius, borderRightColor, borderRightWidth, borderStyle, borderTopColor, borderTopLeftRadius, borderTopRightRadius, borderTopWidth, borderWidth, ...others} = fs
    let style = {flexDirection, alignItems, justifyContent, margin, marginBottom, marginHorizontal, marginLeft, marginRight, marginTop, marginVertical, padding, paddingBottom, paddingHorizontal, paddingLeft, paddingRight, paddingTop, paddingVertical, backgroundColor, borderBottomColor, borderBottomLeftRadius, borderBottomRightRadius, borderBottomWidth, borderColor, borderLeftColor, borderLeftWidth, borderRadius, borderRightColor, borderRightWidth, borderStyle, borderTopColor, borderTopLeftRadius, borderTopRightRadius, borderTopWidth, borderWidth}
    for (let key in style) {
      if (style[key] === undefined) {
        delete style[key]
      }
    }
    return style
  }

  buildProps () {
    let {style, arrow, paddingCorner, headerStyle, arrowStyle, contentStyle, popoverStyle, ...others} = this.props

    style = [{
      backgroundColor: '#292f4f',
      borderColor: 'rgba(0, 0, 0, 0.15)',
      borderRadius: 4,
      borderWidth: pixelSize
    }].concat(style)

    let fs = StyleSheet.flatten(style)
    let {backgroundColor, borderColor, borderRadius, borderWidth} = fs

    let arrowSize = 7 // Square side length
    let headerSize = Math.sqrt(arrowSize * arrowSize * 2) / 2 + borderWidth // The half-length of the square diagonal: sqrt(7^2 + 7^2) / 2 = 4.95
    let headerPadding = headerSize - arrowSize / 2 // Let the center of square on the edge: 5 - (7 / 2) = 1.5
    let headerPaddingCorner = paddingCorner || 8
    let contentPadding = headerSize - borderWidth

    let headerLayouts = {
      none: {},
      topLeft: {top: 0, left: 0, right: 0, height: headerSize, paddingTop: headerPadding, alignItems: 'flex-start', paddingLeft: headerPaddingCorner},
      top: {top: 0, left: 0, right: 0, height: headerSize, paddingTop: headerPadding, alignItems: 'center'},
      topRight: {top: 0, left: 0, right: 0, height: headerSize, paddingTop: headerPadding, alignItems: 'flex-end', paddingRight: headerPaddingCorner},
      rightTop: {top: 0, bottom: 0, right: 0, width: headerSize, paddingRight: headerPadding, alignItems: 'flex-end', justifyContent: 'flex-start', paddingTop: headerPaddingCorner},
      right: {top: 0, bottom: 0, right: 0, width: headerSize, paddingRight: headerPadding, alignItems: 'flex-end', justifyContent: 'center'},
      rightBottom: {top: 0, bottom: 0, right: 0, width: headerSize, paddingRight: headerPadding, alignItems: 'flex-end', justifyContent: 'flex-end', paddingBottom: headerPaddingCorner},
      bottomRight: {bottom: 0, left: 0, right: 0, height: headerSize, paddingBottom: headerPadding, alignItems: 'flex-end', justifyContent: 'flex-end', paddingRight: headerPaddingCorner},
      bottom: {bottom: 0, left: 0, right: 0, height: headerSize, paddingBottom: headerPadding, alignItems: 'center', justifyContent: 'flex-end'},
      bottomLeft: {bottom: 0, left: 0, right: 0, height: headerSize, paddingBottom: headerPadding, alignItems: 'flex-start', justifyContent: 'flex-end', paddingLeft: headerPaddingCorner},
      leftBottom: {top: 0, bottom: 0, left: 0, width: headerSize, paddingLeft: headerPadding, alignItems: 'flex-start', justifyContent: 'flex-end', paddingBottom: headerPaddingCorner},
      left: {top: 0, bottom: 0, left: 0, width: headerSize, paddingLeft: headerPadding, alignItems: 'flex-start', justifyContent: 'center'},
      leftTop: {top: 0, bottom: 0, left: 0, width: headerSize, paddingLeft: headerPadding, alignItems: 'flex-start', justifyContent: 'flex-start', paddingTop: headerPaddingCorner}
    }
    let arrowLayouts = {
      none: {},
      topLeft: {transform: [{rotate: '45deg'}]},
      top: {transform: [{rotate: '45deg'}]},
      topRight: {transform: [{rotate: '45deg'}]},
      rightTop: {transform: [{rotate: '135deg'}]},
      right: {transform: [{rotate: '135deg'}]},
      rightBottom: {transform: [{rotate: '135deg'}]},
      bottomRight: {transform: [{rotate: '225deg'}]},
      bottom: {transform: [{rotate: '225deg'}]},
      bottomLeft: {transform: [{rotate: '225deg'}]},
      leftBottom: {transform: [{rotate: '315deg'}]},
      left: {transform: [{rotate: '315deg'}]},
      leftTop: {transform: [{rotate: '315deg'}]}
    }
    let popoverLayouts = {
      none: {},
      topLeft: {paddingTop: contentPadding},
      top: {paddingTop: contentPadding},
      topRight: {paddingTop: contentPadding},
      rightTop: {paddingRight: contentPadding},
      right: {paddingRight: contentPadding},
      rightBottom: {paddingRight: contentPadding},
      bottomRight: {paddingBottom: contentPadding},
      bottom: {paddingBottom: contentPadding},
      bottomLeft: {paddingBottom: contentPadding},
      leftBottom: {paddingLeft: contentPadding},
      left: {paddingLeft: contentPadding},
      leftTop: {paddingLeft: contentPadding}
    }

    if (!arrow) arrow = 'none'
    let useArrow = arrow
    switch (arrow) {
      case 'topLeft':
      case 'topRight':
        if (headerPaddingCorner + contentPadding > this.state.width / 2) useArrow = 'top'
        break
      case 'rightTop':
      case 'rightBottom':
        if (headerPaddingCorner + contentPadding > this.state.height / 2) useArrow = 'right'
        break
      case 'bottomRight':
      case 'bottomLeft':
        if (headerPaddingCorner + contentPadding > this.state.width / 2) useArrow = 'bottom'
        break
      case 'leftBottom':
      case 'leftTop':
        if (headerPaddingCorner + contentPadding > this.state.height / 2) useArrow = 'left'
        break
    }

    headerStyle = Object.assign({
      position: 'absolute',
      overflow: 'hidden',
      backgroundColor: 'rgba(0, 0, 0, 0)'
    }, headerLayouts[useArrow])
    arrowStyle = Object.assign({
      backgroundColor,
      width: arrowSize,
      height: arrowSize,
      borderColor,
      borderTopWidth: borderWidth,
      borderLeftWidth: borderWidth
    }, arrowLayouts[useArrow])
    contentStyle = this.filterContentStyle(fs)
    popoverStyle = [this.filterPopoverStyle(fs, useArrow === 'none'), {
      backgroundColor: useArrow === 'none' ? '#292f4f' : 'rgba(0, 0, 0, 0)' // Transparent background will cause a warning at debug mode
    }].concat(popoverLayouts[useArrow])

    this.props = {style, arrow, paddingCorner, headerStyle, arrowStyle, contentStyle, popoverStyle, ...others}
  }

  onLayout (e) {
    let {width, height} = e.nativeEvent.layout
    if (width != this.state.width || height != this.state.height) {
      this.setState({width, height})
    }
    this.props.onLayout && this.props.onLayout(e)
  }

  render () {
    this.buildProps()

    let {style, arrow, headerStyle, arrowStyle, contentStyle, popoverStyle, children, onLayout, ...others} = this.props
    return (
      <View style={popoverStyle} onLayout={(e) => this.onLayout(e)} {...others}>
        <View style={contentStyle} activeOpacity={1}>
          {children}
        </View>
        {!arrow || arrow === 'none' ? null
          : <View style={headerStyle}>
            <View style={arrowStyle} />
          </View>
        }
      </View>
    )
  }
}
